#include <iostream>
#include "Sock.hpp"
#include <sys/epoll.h>
#include <string>

#define SIZE 128
#define NUM 64

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        std::cout << "Usage: \n\t" << argv[0] << " port" << std::endl;
        exit(1);
    }
    // 1. 建立tcp 监听socket
    uint16_t port = (uint16_t)atoi(argv[1]);
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock);

    // 2. 创建epoll模型，获得epfd(文件描述符)
    int epfd = epoll_create(SIZE);

    // 3. 先将listen_sock和它所关心的事件，添加到内核
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;

    epoll_ctl(epfd, EPOLL_CTL_ADD, listen_sock, &ev);
    cout << epfd << endl;
    // 4. 事件循环
    struct epoll_event revs[NUM];
    for (;;)
    {
        int timeout = 1000;
        int n = epoll_wait(epfd, revs, NUM, timeout);
        cout << n << endl;
        switch (n)
        {
        case -1:
            std::cerr << "epoll error" << std::endl;
            break;
        case 0:
            std::cout << "epoll timeout" << std::endl;
            break;
        default:
            cout << "有fd对应的事件就绪啦!" << std::endl;
            for (int i = 0; i < n; i++) // epoll：wait得到的都是已经就绪的事件
            {
                int sock = revs[i].data.fd;
                if (revs[i].events & EPOLLIN) //读事件就绪
                {
                    if (sock == listen_sock) //监听IO
                    {
                        std::cout << "文件描述符: " << sock << " 链接数据就绪" << std::endl;
                        int fd = Sock::Accept(listen_sock);
                        if (fd >= 0)
                        {
                            std::cout << "获取新链接成功啦： " << fd << std::endl;
                            struct epoll_event fd_ev;
                            fd_ev.events = EPOLLIN;
                            fd_ev.data.fd = fd;
                            epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &fd_ev); //新的fd托管给了epoll！
                            std::cout << "已经将" << fd << " 托管给epoll啦" << std::endl;
                        }
                        else
                        {
                        }
                    }
                    else //普通IO
                    {
                        std::cout << "文件描述符: " << sock << "正常数据就绪" << std::endl;
                        char buffer[1024];
                        ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
                        if (s > 0)
                        {
                            buffer[s] = 0;
                            std::cout << "client [" << sock << "]# " << buffer << std::endl;

                            //这里可以进行封装响应
                            //将我们的关心事件更改成为EPOLLOUT写事件
                            // struct epoll_event _ev;
                            // _ev.events = EPOLLOUT;
                            // _ev.data.fd = sock;
                            // epoll_ctl(epfd, EPOLL_CTL_MOD, sock, &_ev);
                        }
                        else if (s == 0)
                        {
                            //对端关闭链接
                            std::cout << "client quit " << sock << std::endl;
                            close(sock);
                            epoll_ctl(epfd, EPOLL_CTL_DEL, sock, nullptr);
                            std::cout << "sock: " << sock << "delete from epoll success" << std::endl;
                        }
                        else
                        {
                            //读取失败
                            std::cout << "recv error" << std::endl;
                            close(sock);
                            epoll_ctl(epfd, EPOLL_CTL_DEL, sock, nullptr);
                            std::cout << "sock: " << sock << "delete from epoll success" << std::endl;
                        }
                    }
                }
                else if (revs[i].events & EPOLLOUT) //写时间就绪
                {
                }
                else //其他事件就绪
                {
                }
            }
            break;
        }
    }
    close(epfd);
    close(listen_sock);
    return 0;
}
